package com.pig4cloud.pig.ads.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.pig4cloud.pig.ads.clickhouse3399.mapper.AdFreeCrowdPack;
import com.pig4cloud.pig.ads.dao.AdFreeCrowdPackDao;
import com.pig4cloud.pig.ads.gdt.service.GdtCustomAudiencesService;
import com.pig4cloud.pig.ads.pig.mapper.SelfCustomAudienceMapper;
import com.pig4cloud.pig.ads.pig.mapper.SelfCustomAudiencePushLogMapper;
import com.pig4cloud.pig.ads.service.SelfCustomAudienceService;
import com.pig4cloud.pig.ads.service.TtCustomAudienceService;
import com.pig4cloud.pig.ads.utils.DateUtils;
import com.pig4cloud.pig.api.dto.FreeCrowdPackDto;
import com.pig4cloud.pig.api.entity.SelfCustomAudience;
import com.pig4cloud.pig.api.entity.SelfCustomAudiencePushLog;
import com.pig4cloud.pig.api.util.JsonUtil;
import com.pig4cloud.pig.api.vo.FreeCrowdPackVo;
import com.pig4cloud.pig.api.vo.FreeCrowdVo;
import com.pig4cloud.pig.common.core.constant.enums.PlatformTypeEnum;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import com.pig4cloud.pig.api.dto.SelfCustomAudiencePushDTO;
import com.pig4cloud.pig.api.dto.TtCustomAudiencesDTO;
import com.pig4cloud.pig.api.enums.CustomAudienceTypeEnum;
import com.pig4cloud.pig.api.enums.SelfCustomAudienceTypeEnum;
import com.pig4cloud.pig.api.gdt.dto.GdtCustomAudiencesDto;
import com.pig4cloud.pig.api.util.RandomUtil;
import com.sjda.framework.common.utils.ModelResult;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;


@Log4j2
@Service
@RequiredArgsConstructor
public class SelfCustomAudienceServiceImpl extends ServiceImpl<SelfCustomAudienceMapper, SelfCustomAudience> implements SelfCustomAudienceService {

	private final TtCustomAudienceService ttCustomAudienceService;

	private final GdtCustomAudiencesService gdtCustomAudiencesService;

	@Autowired
	private AdFreeCrowdPack adFreeCrowdPack;

	@Autowired
	private AdFreeCrowdPackDao adFreeCrowdPackDao;

	@Autowired
	private SelfCustomAudiencePushLogMapper selfCustomAudiencePushLogMapper;

//	@Autowired
//	private SelfCustomAudienceMapperExt selfCustomAudienceMapperExt;
	/**
	 * 自有人群包下载地址
	 */
	@Value("${self_custom_audience_download_url:http://test.pangu.3399.com/upload/selfCustomAudience/}")
	private String downloadUrl;

	/**
	 * 自有人群包上传地址
	 * /data/wwwroot/ad-platform/upload/selfCustomAudience/
	 */
	@Value("${self_custom_audience_upload_path:/data/wwwroot/ad-platform/upload/selfCustomAudience/}")
	private String uploadPath;

	@SneakyThrows
	@Override
	public R push(SelfCustomAudiencePushDTO dto) {
		//记录推送失败的账户
		Map<String, String> failMap = Maps.newHashMap();
		//记录成功的账户
		Map<String, Object> successMap = Maps.newHashMap();
		//批量推送
		for (Long id : dto.getIds()) {
			SelfCustomAudience selfCustomAudience = getBaseMapper().selectById(id);
			if (selfCustomAudience == null) {
				failMap.put(id + "", "人群包不存在");
				continue;
//				return R.failed("人群包不存在");
			}

			if ((dto.getMediaType() == Integer.parseInt(PlatformTypeEnum.TT.getValue()))) {
				//校验类型
				List<Integer> ttTypeList = Lists.newArrayList(0,1,2,4,5,6,7,8);
				if (!ttTypeList.contains(selfCustomAudience.getType())) {
					failMap.put(id + "", "存在不支持的类型("+SelfCustomAudienceTypeEnum.getNameByKey(selfCustomAudience.getType())+")");
					break;
				}

				TtCustomAudiencesDTO tt = new TtCustomAudiencesDTO();
				tt.setDownUrl(selfCustomAudience.getDownloadUrl());
				tt.setData_source_name(selfCustomAudience.getName());
				tt.setDescription(selfCustomAudience.getName() + "(自有人群包)");
				tt.setDataType(selfCustomAudience.getType());

				for (String advertiserId : dto.getAdvertiserIds()) {
					tt.setAdvertiser_id(advertiserId);
					//校验当前人群包在指定媒体的广告账户下是否推送
					if (checkHasPush(id, dto.getMediaType(), advertiserId)) {
						failMap.put(id + "_" + advertiserId, "广告账户(" + advertiserId + "),已经推送过人群包(" + selfCustomAudience.getName() + ")");
						continue;
					}
					R result = ttCustomAudienceService.pushSelfCustomAudience(tt);
					if (result.getCode() == 0) {
						successMap.put(id + "_" + advertiserId, result.getData());
						this.insertPushLog(id, dto.getMediaType(), advertiserId);
					} else {
						failMap.put(id + "_" + advertiserId, result.getMsg());
					}
				}
			} else if ((dto.getMediaType() == Integer.parseInt(PlatformTypeEnum.GDT.getValue()))) {
				GdtCustomAudiencesDto gdt = new GdtCustomAudiencesDto();
				// 类型校验  UID(2,"UID") 广点通不支持 UID
				if (selfCustomAudience.getType() == 2) {
					failMap.put(id + "", "存在不支持的类型("+SelfCustomAudienceTypeEnum.getNameByKey(selfCustomAudience.getType())+")");
					break;
				}

				gdt.setName(selfCustomAudience.getName());
				gdt.setDescription(selfCustomAudience.getName() + "(自有人群包)");
				//默认号码文件人群
				gdt.setType(CustomAudienceTypeEnum.CUSTOMER_FILE.getKey());
				//转换为广点通所需要的userIdType
				gdt.setUserIdType(SelfCustomAudienceTypeEnum.getNameByKey(selfCustomAudience.getType()));
				//TXT(文件每行一个号码文件) 转 ZIP
				String downUrl = selfCustomAudience.getDownloadUrl();
				//获取上传时的文件时间路径
				String dataPath = downUrl.substring(downUrl.lastIndexOf("/") - 8, downUrl.lastIndexOf("/"));
				//获取txt文件名
				String srcFile = downUrl.substring(downUrl.lastIndexOf("/") + 1);
				String zipFileName = DateUtils.dateToString(new Date(), DateUtils.YYYYMMDDHHMMSS) + "_" + SecurityUtils.getUser().getId() + "_" + RandomUtil.generateNumStr(4) + ".zip";
				//压缩文件生成在同目录
				String filePathName = uploadPath + dataPath + File.separator + zipFileName;
				File zip = ZipUtil.zip(uploadPath + dataPath + File.separator + srcFile, filePathName);
				//广点通限定大小为100M
				if (checkFileSize4Gdt(zip)) {
					failMap.put(id + "", "压缩文件不能大于100M");
					continue;
					//return R.failed("压缩文件不能大于100M");
				}
				for (String advertiserId : dto.getAdvertiserIds()) {
					gdt.setAccountId(advertiserId);
					R result = gdtCustomAudiencesService.pushSelfCustomAudience(zip, gdt);
					if (checkHasPush(id, dto.getMediaType(), advertiserId)) {
						failMap.put(id + "_" + advertiserId, "广告账户(" + advertiserId + "),已经推送过人群包(" + selfCustomAudience.getName() + ")");
						continue;
					}
					if (result.getCode() == 0) {
						successMap.put(id + "_" + advertiserId, result.getData());
						this.insertPushLog(id, dto.getMediaType(), advertiserId);
					} else {
						failMap.put(id + "_" + advertiserId, result.getMsg());
					}
				}
			}
		}
		if (CollectionUtils.isEmpty(failMap)) {
			return R.ok(successMap);
		} else {
//			return R.failed(failMap, "人群包推送失败,请稍后再试!");
			//因前端框架原因，现将错误信息转JSONString并赋值msg
			return R.failed(failMap, JSON.toJSONString(failMap.values()));
		}
	}

	private boolean checkHasPush(Long id, Integer mediaType, String advertiserId) {
		//校验当前人群包在指定媒体的广告账户下是否推送
		SelfCustomAudiencePushLog pushLog = selfCustomAudiencePushLogMapper.selectOne(Wrappers.<SelfCustomAudiencePushLog>query().lambda()
				.and(wrapper -> wrapper.eq(SelfCustomAudiencePushLog::getSelfId, id))
				.and(wrapper -> wrapper.eq(SelfCustomAudiencePushLog::getMediaType, mediaType))
				.and(wrapper -> wrapper.eq(SelfCustomAudiencePushLog::getAdvertiserId, advertiserId)));
		if (pushLog != null) {
			return true;
		} else {
			return false;
		}
	}

	private int insertPushLog(Long id, Integer mediaType, String advertiserId) {
		//记录推送日志
		SelfCustomAudiencePushLog entity = new SelfCustomAudiencePushLog();
		entity.setAdvertiserId(advertiserId);
		entity.setSelfId(id);
		entity.setMediaType(mediaType);
		entity.setCreateId(Long.valueOf(SecurityUtils.getUser().getId()));
		entity.setUpdateId(Long.valueOf(SecurityUtils.getUser().getId()));
		return selfCustomAudiencePushLogMapper.insert(entity);
	}

	private Boolean checkFileSize4Gdt(File file) {
		long Kb = 1 * 1024;
		long Mb = Kb * 1024;
		long Gb = Mb * 1024;
		long size = FileUtil.size(file);
		if (size >= Mb && size < Gb) {
			double len = file.length() / Mb;
			if (len > 100) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 列表
	 *
	 * @param dto
	 * @return
	 */
	@Override
	public R getFreeCrowdList(FreeCrowdPackDto dto) {
		List<FreeCrowdVo> list = adFreeCrowdPackDao.getFreeCrowdList(dto);
		return R.ok(list);
	}

	/**
	 * 自由人群包列表
	 *
	 * @param pg
	 * @param dto
	 * @return
	 */
	@Override
	public IPage<FreeCrowdVo> getFreeCrowdPackList(Page pg, FreeCrowdPackDto dto) {
		IPage<FreeCrowdVo> pge = new Page();
		pge.setSize(pg.getSize());
		pge.setCurrent(pg.getCurrent());
		IPage<FreeCrowdVo> page = adFreeCrowdPack.getFreeCrowdPackListPage(pge, dto);
		return page;
	}

	/**
	 * 自有人群包 打包
	 *
	 * @param dto
	 * @return
	 */
	@Override
	public R downloadFreeCrowdPack(FreeCrowdPackDto dto) {
		//根据条件获取自有人群集合
		// TODO
		List<FreeCrowdPackVo> list = adFreeCrowdPackDao.getFreeCrowdListPack(dto);

		//获取到需要打包的字段
		List<String> fieldList = Lists.newArrayList();
		List<String> fieldListResult = this.setData(list, dto.getType(), fieldList);

		if (ObjectUtils.isEmpty(fieldListResult)) {
			return R.failed("当前选择字段内容为空，打包失败");
		}
		// 这里仅仅上传TXT文件，压缩逻辑和分多个txt逻辑在自有人群包推送逻辑中处理

		//生成TXT文件 打包字段不为空的 可以生成txt文件
		Map<String, Object> result = genTxtFile(fieldListResult);

		// 因头条现在压缩包大小为 50M，广点通现在为 100M，故上传TXT暂时限制100M
		File txtFile = (File) result.get("file");


		if (checkFileSize4Gdt(txtFile)) {
			//文件大于100M，需要删除文件
			FileUtil.del(txtFile);
			return R.failed("文件内容不能大于100M");
		}

		//持久化到DB
		SelfCustomAudience entity = new SelfCustomAudience();
		entity.setName(dto.getPackageName());
		entity.setType(dto.getType());
		entity.setDataCount((Integer) result.get("fileSize"));
		entity.setDownloadUrl((String) result.get("downLoadUrl"));
		entity.setCreateId(Long.valueOf(SecurityUtils.getUser().getId()));
		entity.setUpdateId(Long.valueOf(SecurityUtils.getUser().getId()));
		boolean flag = save(entity);
		if (flag) {
			return R.ok();
		} else {
			return R.failed("打包失败");
		}
	}


	private Map<String, Object> genTxtFile(List<String> fieldListResult) {
		Map<String, Object> resultMap = Maps.newHashMap();
		String ymPath = DateUtils.dateToString(new Date(), DateUtils.YYYYMMDD) + "/";
		UUID uid = UUID.randomUUID();
		String uuid = String.valueOf(uid);
		String realName = uuid + ".txt";
		String filePath = uploadPath + ymPath + realName;
		String downLoadUrl = downloadUrl + ymPath + realName;
		File file = FileUtil.writeLines(fieldListResult, filePath, "UTF-8");

		resultMap.put("downLoadUrl", downLoadUrl);
		resultMap.put("fileSize", fieldListResult.size());
		resultMap.put("file", file);

		return resultMap;
	}

	private List<String> setData(List<FreeCrowdPackVo> list, Integer type, List<String> fieldList) {
		switch (type) {
			case 0:
				fieldList = list.stream().map(FreeCrowdPackVo::getImei).filter(Objects::nonNull).collect(Collectors.toList());
				break;
			case 1:
				fieldList = list.stream().map(FreeCrowdPackVo::getIdfa).filter(Objects::nonNull).collect(Collectors.toList());
				break;
			case 2:
				//自有人群包没有UID字段
				Lists.newArrayList();
				break;
			case 4:
				List<String> tmpImei = list.stream().map(FreeCrowdPackVo::getImei).filter(Objects::nonNull).collect(Collectors.toList());
				fieldList = tmpImei.stream().map(v -> DigestUtil.md5Hex(v.toLowerCase())).collect(Collectors.toList());
				break;
			case 5:
				List<String> tmpIdfa = list.stream().map(FreeCrowdPackVo::getIdfa).filter(Objects::nonNull).collect(Collectors.toList());
				fieldList = tmpIdfa.stream().map(v -> DigestUtil.md5Hex(v.toUpperCase())).collect(Collectors.toList());
				break;
			case 6:
				List<String> tmpMobile = list.stream().map(FreeCrowdPackVo::getMobile).filter(Objects::nonNull).collect(Collectors.toList());
				fieldList = tmpMobile.stream().map(v -> DigestUtil.sha256Hex(v)).collect(Collectors.toList());
				break;
			case 7:
				fieldList = list.stream().map(FreeCrowdPackVo::getOaid).filter(Objects::nonNull).collect(Collectors.toList());
				break;
			case 8:
				List<String> tmpOaid = list.stream().map(FreeCrowdPackVo::getOaid).filter(Objects::nonNull).collect(Collectors.toList());
				fieldList = tmpOaid.stream().map(v -> DigestUtil.md5Hex(v.toLowerCase())).collect(Collectors.toList());
				break;
			case 9:
				List<String> tmpMobileMd5 = list.stream().map(FreeCrowdPackVo::getMobile).filter(Objects::nonNull).collect(Collectors.toList());
				fieldList = tmpMobileMd5.stream().map(v -> DigestUtil.md5Hex(v)).collect(Collectors.toList());
				break;
			case 10:
				fieldList = list.stream().map(FreeCrowdPackVo::getMac).filter(Objects::nonNull).collect(Collectors.toList());
				break;
			default:
				log.error("不支持的打包类型-{}", type);
		}
		return fieldList;
	}


}
